import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JMenuBar;
import java.awt.Toolkit;
import javax.swing.JScrollPane;
import java.awt.datatransfer.*;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.File;
import java.io.IOException;
import javax.swing.JFileChooser;
import java.util.Enumeration;
import java.util.Hashtable;

public class Machine extends Container
  {
  private Slate slate;
  private FileMenu fileMenu;
  private MachineMenu machineMenu;
  private Tape tape;
  private JFileChooser fileChooser;

  private String version="1";

  public Machine(String s)
    {
    slate=new Slate(this);
    fileMenu=new FileMenu(this);
    machineMenu=new MachineMenu(this);
    tape=new Tape(this);
    fileChooser=new JFileChooser(".");

    setLayout(new BorderLayout());    
    JScrollPane sp=new JScrollPane(slate,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    add(sp,"Center");
    JMenuBar c=new JMenuBar();
    add(c,"North");
    c.add(fileMenu);
    c.add(tape);  
    c.add(machineMenu);

    fromString(s);
    }

  public String toString()
    {
    String automaString=null;
    GGraph graph=slate.getGraph();
    if(graph.isLocked())
      {
      automaString="";
      String versionString="version:\n"+version;
      String typeString="";
      int type=graph.getType();
      if(type==0) typeString="type:\nautomaton";
      else if(type==1) typeString="type:\nmealy";
      else if(type==2) typeString="type:\nturing";
      String statesString="states:";
      String transitionsString="transitions:";
      String acceptingString="accepting states:";
      String startingString="starting state:";
      String currentStateString="current state:";
      String leftTapeString="left tape:\n"+tape.getLeft();
      String rightTapeString="right tape:\n"+tape.getRight();
      Enumeration states=graph.states();
      while(states.hasMoreElements())
        {
        State state=(State)states.nextElement();
        statesString=statesString+"\n"+state.toString();
        if(state==graph.getStart()) startingString=startingString+"\n"+state.getLabel();
        if(state.isAccepting()) acceptingString=acceptingString+"\n"+state.getLabel();
        if(state==graph.getSelected()) currentStateString=currentStateString+"\n"+state.getLabel();
        Hashtable transitions=graph.exitingTransitions(state);
        Enumeration reads=transitions.keys();
        while(reads.hasMoreElements())
          {
          String read=(String)reads.nextElement();
          Transition transition=(Transition)transitions.get(read);
          transitionsString=transitionsString+"\n"+transition.toString();
          }
        }
      automaString=versionString+"\n"+typeString+"\n"+statesString+"\n"+startingString+"\n"+acceptingString+"\n"+transitionsString+"\n"+leftTapeString+"\n"+rightTapeString+"\n"+currentStateString;
      }
    return automaString;
    }
  
  public String toPisa()
    {
    String transitionsString=null;
    GGraph graph=slate.getGraph();
    if(graph.isLocked())
      {
      transitionsString="";
      Enumeration states=graph.states();
      while(states.hasMoreElements())
        {
        State state=(State)states.nextElement();
        Hashtable transitions=graph.exitingTransitions(state);
        Enumeration reads=transitions.keys();
        while(reads.hasMoreElements())
          {
          String read=(String)reads.nextElement();
          Transition transition=(Transition)transitions.get(read);
          transitionsString=transitionsString+transition.toPisa()+"\n";
          }
        }
      }
    return transitionsString;
    }

  public void fromString(String s)
    {
    GGraph graph=slate.getGraph();
    int p=-1;
    String[] g=s.split("\r\n|\r|\n");
    for(int i=0;i<g.length;i++)
      {
      if(g[i].equals("version:")) p=-2;
      else if(g[i].equals("type:")) p=-1;
      else if(g[i].equals("states:")) p=0;
      else if(g[i].equals("transitions:")) p=1;
      else if(g[i].equals("accepting states:")) p=2;
      else if(g[i].equals("starting state:")) p=3;
      else if(g[i].equals("left tape:")) p=4;
      else if(g[i].equals("right tape:")) p=5;
      else if(g[i].equals("current state:")) p=6;
      else
        {
        if(p==-2) version=g[i];
        else if(p==-1)
          {
          if(g[i].equals("automaton")) graph.setType(0);
          else if(g[i].equals("mealy")) graph.setType(1);
          else if(g[i].equals("turing")) graph.setType(2);
          }
        else if(p==0) graph.addCircle(g[i]);
        else if(p==1) graph.addTransition(g[i]);
        else if(p==2) graph.getCircle(g[i]).setAccepting(true);
        else if(p==3)
          {
          graph.setStart(graph.getCircle(g[i]));
          }
        else if(p==4)
          {
          tape.setLeft(g[i]);
          }
        else if(p==5)
          {
          tape.setRight(g[i]);
          }
        else if(p==6)
          {
          graph.setSelected(graph.getCircle(g[i]));
          }
        }
      }
    }
  public void copy(boolean pisa)
    {
    GGraph graph=slate.getGraph();
    //slate.tryToLock();
    if(graph.isLocked())
      {
      String s;
      if(pisa) s=toPisa();
      else  s=toString();
      StringSelection contents=new StringSelection(s);
      Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents,null);
      }
    }
  public void paste()
    {
    Transferable content=Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
    try
      {
      String s=(String)content.getTransferData(DataFlavor.stringFlavor);
      slate.setGraph(new GGraph(this));
      fromString(s);
      slate.repaint();
      }
    catch(Throwable exc)
      {
      }
    }
  public void open()
    {
    int returnValue=fileChooser.showOpenDialog(this);
    if(returnValue==JFileChooser.APPROVE_OPTION)
      {
      File file=fileChooser.getSelectedFile();
      try
        {
        InputStream is=new FileInputStream(file);
        byte[] b=new byte[(int)file.length()];
        is.read(b);
        slate.setGraph(new GGraph(this));
        fromString(new String(b));
        }
      catch(IOException ex)
        {
        }
      }
    }
  public void save(boolean pisa)
    {
    GGraph graph=slate.getGraph();
    //graph.tryToLock();
    if(graph.isLocked())
      {
      int returnValue=fileChooser.showSaveDialog(this);
      if(returnValue==JFileChooser.APPROVE_OPTION)
        {
        File file=fileChooser.getSelectedFile();
        try
          {
          OutputStream os=new FileOutputStream(file);
          String s;
          if(pisa) s=toPisa();
          else  s=toString();
          byte[] b=s.getBytes();
          os.write(b);
          }
        catch(Exception ex)
          {
          }
        }
      }
    }
  public void step()
    {
    GGraph graph=slate.getGraph();
    //slate.tryToLock();
    if(graph.isLocked())
      {
      slate.repaint();
      if(graph.selectTransition(tape.getSymbol()))
        {
        String w=graph.getTransition().getWrite();
        int m=graph.getTransition().getMove();
        graph.selectNextCircle();
        graph.unselectTransition();
        tape.step(w,m);
        }
      }
    }
  public void setStart()
    {
    slate.setStart();
    }
  public void changeAccepting()
    {
    slate.changeAccepting();
    }
  public void tryToLock()
    {
    GGraph graph=slate.getGraph();
    graph.tryToLock();
    if(graph.isLocked())
      {
      setEnabled(true);
      }
    }
  public void unlock()
    {
    GGraph graph=slate.getGraph();
    graph.unlock();
    setEnabled(false);
    }
  public void setEnabled(boolean set)
    {
    fileMenu.setEnabled(set);
    tape.setEnabled(set);
    machineMenu.setEnabled(set);
    }
  }
